#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QLabel>
#include <QGridLayout>
#include <QImage>
#include <QWidget>
#include <QMessageBox>
#include <QFileDialog>
#include <QFile>
#include <QTimer>

#include "settings.h"
#include "moveapplication.h"
#include "xmltrajectoryreader.h"
#include "xmltrajectorywriter.h"
#include "trackseditor.h"
#include "staticgesturedialog.h"
#include "mainstaticgesturedialog.h"
#include "hmmprogressdialog.h"
#include "hmmstatesdialog.h"

#include <mlgesture.h>
#include <gestureutils.h>
#include <hmm.h>

#include <iostream>
using namespace std;

using namespace mlgesture;

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
	ui_(new Ui::MainWindow)
{
	ui_->setupUi(this);
	camId_ = MoveApplication::getCamIdSettings();
	MoveApplication::getSkinThresholds(lowerSkinThreshold_, upperSkinThreshold_);
	MoveApplication::getValueThreshold(lowerValueThreshold_, upperValueThreshold_);
	adaptThresholdActive_ = MoveApplication::getAdaptThreshold();
	trajectory_ = new TrajectoryCapture(camId_, &trajectories_);
	staticWindow_ = NULL;

	frameTimer_ = new QTimer();
	connect(frameTimer_, SIGNAL(timeout()), trajectory_, SLOT(frameProcess()));
	connect(frameTimer_, SIGNAL(timeout()), this, SLOT(updateLabels()));

	trackingTimer_ = new QTimer();
	connect(trackingTimer_, SIGNAL(timeout()), trajectory_, SLOT(trajectoryProcess()));

	saved_ = true;

	createLayout();
	createActions();
	createMenu();
}

MainWindow::~MainWindow()
{
	delete frameTimer_;
	delete trackingTimer_;
	delete ui_;
	delete trajectory_;
}

void MainWindow::createActions() {
	newAct_ = new QAction(tr("&Nowy plik"), this);
	newAct_->setShortcut(tr("Ctrl+N"));
	connect(newAct_, SIGNAL(triggered()), this, SLOT(newFile()));

	openAct_ = new QAction(tr("&Otwórz"), this);
	openAct_->setShortcut(tr("Ctrl+O"));
	connect(openAct_, SIGNAL(triggered()), this, SLOT(open()));

	saveAct_ = new QAction(tr("Za&pisz"), this);
	saveAct_->setShortcut(tr("Ctrl+S"));
	connect(saveAct_, SIGNAL(triggered()), this, SLOT(save()));

	saveAsAct_ = new QAction(tr("Zapisz &jako..."), this);
	connect(saveAsAct_, SIGNAL(triggered()), this, SLOT(saveAs()));

	closeAct_ = new QAction(tr("&Zamknij"), this);
	closeAct_->setShortcut(tr("Ctrl+Q"));
	connect(closeAct_, SIGNAL(triggered()), this, SLOT(close()));

	startAct_ = new QAction(tr("&Rozpocznij śledzenie"), this);
	startAct_->setShortcut(tr("Ctrl+X"));
	connect(startAct_, SIGNAL(triggered()), this, SLOT(startTracking()));

	stopAct_ = new QAction(tr("&Zatrzymaj śledzenie"), this);
	stopAct_->setShortcut(tr("Ctrl+T"));
	stopAct_->setDisabled(true);
	connect(stopAct_, SIGNAL(triggered()), this, SLOT(stopTracking()));

	editAct_ = new QAction(tr("&Edytuj ścieżki"), this);
	editAct_->setShortcut(tr("Ctrl+E"));
	connect(editAct_, SIGNAL(triggered()), this, SLOT(editTracks()));

	settingsAct_ = new QAction(tr("Ustawienia"), this);
	settingsAct_->setShortcut(tr("Ctrl+L"));
	connect(settingsAct_, SIGNAL(triggered()), this, SLOT(showSettings()));

	aboutAct_ = new QAction(tr("&O programie"), this);
	connect(aboutAct_, SIGNAL(triggered()), this, SLOT(about()));

	staticAct_ = new QAction(tr("Rejestruj statyczne gesty"), this);
	staticAct_->setShortcut(tr("Ctrl+G"));
	connect(staticAct_, SIGNAL(triggered()), this, SLOT(showStatics()));

	captureAct_ = new QAction(tr("Pobierz gest"), this);
	captureAct_->setShortcut(tr("Ctrl+C"));
	captureAct_->setDisabled(true);
	connect(captureAct_, SIGNAL(triggered()), this, SLOT(captureStatic()));

	hmmAct_ = new QAction(tr("Hmm"), this);
	connect(hmmAct_, SIGNAL(triggered()), this, SLOT(hmm()));

	trainStaticAct_ = new QAction(tr("Trenuj gesty statyczne"), this);
	connect(trainStaticAct_, SIGNAL(triggered()), this, SLOT(trainStatic()));
}

void MainWindow::createMenu() {
	fileMenu_ = new QMenu(tr("&Plik"), this);
	fileMenu_->addAction(newAct_);
	fileMenu_->addAction(openAct_);
	fileMenu_->addAction(saveAct_);
	fileMenu_->addAction(saveAsAct_);
	fileMenu_->addSeparator();
	fileMenu_->addAction(closeAct_);

	actionMenu_ = new QMenu(tr("&Akcje"));
	actionMenu_->addAction(startAct_);
	actionMenu_->addAction(stopAct_);
	actionMenu_->addAction(editAct_);
	actionMenu_->addSeparator();
	actionMenu_->addAction(staticAct_);
	actionMenu_->addAction(captureAct_);
	actionMenu_->addSeparator();
	actionMenu_->addAction(hmmAct_);
	actionMenu_->addAction(trainStaticAct_);
	actionMenu_->addSeparator();
	actionMenu_->addAction(settingsAct_);

	helpMenu_ = new QMenu(tr("P&omoc"), this);
	helpMenu_->addAction(aboutAct_);

	menuBar()->addMenu(fileMenu_);
	menuBar()->addMenu(actionMenu_);
	menuBar()->addMenu(helpMenu_);
}

void MainWindow::close() {
	QMainWindow::close();
}

void MainWindow::about() {
	QMessageBox::about(this, tr("O programie"),
					   tr("Program do przechwytywania i etykietowania trajektorii ruchu ręki\n\nAutor: Michał Laskowski"));
}

void MainWindow::startTracking() {
	saved_ = false;
	camId_ = MoveApplication::getCamIdSettings();
	MoveApplication::getSkinThresholds(lowerSkinThreshold_, upperSkinThreshold_);
	MoveApplication::getValueThreshold(lowerValueThreshold_, upperValueThreshold_);
	adaptThresholdActive_ = MoveApplication::getAdaptThreshold();
	trackingSensity_ = MoveApplication::getTrackingSensity();
	trajectory_->setParams(camId_, lowerSkinThreshold_, upperSkinThreshold_, lowerValueThreshold_, upperValueThreshold_,
						  adaptThresholdActive_, trackingSensity_);
	trajectory_->activateTracking();
	settingsAct_->setDisabled(true);
	startAct_->setDisabled(true);
	stopAct_->setEnabled(true);
	editAct_->setDisabled(true);
	frameTimer_->start(30);
	trackingTimer_->start(30);
}

void MainWindow::stopTracking() {
	trajectory_->stopTracking();
	frameTimer_->stop();
	trackingTimer_->stop();
	settingsAct_->setEnabled(true);
	stopAct_->setDisabled(true);
	startAct_->setEnabled(true);
	editAct_->setEnabled(true);
}

void MainWindow::showSettings() {
	Settings *s = new Settings(this);
	s->show();
}

void MainWindow::createLayout() {
	mainWidget_ = new QWidget();
	layout_ = new QGridLayout();
	for (int i=0; i<2; i++) {
		for (int j=0; j<2; j++) {
			imgLabels_[i][j] = new QLabel();
			imgLabels_[i][j]->setAlignment(Qt::AlignHCenter);
			layout_->addWidget(imgLabels_[i][j],i,j);
		}
	}

	mainWidget_->setLayout(layout_);
	setCentralWidget(mainWidget_);
}

void MainWindow::updateLabels() {
	imgLabels_[0][0]->setPixmap(QPixmap::fromImage(trajectory_->getFrame()).scaled(imgLabels_[0][0]->size(), Qt::KeepAspectRatio));
	imgLabels_[1][0]->setPixmap(QPixmap::fromImage(trajectory_->getMask()).scaled(imgLabels_[1][0]->size(), Qt::KeepAspectRatio));
	imgLabels_[0][1]->setPixmap(QPixmap::fromImage(trajectory_->getTrajectory()).scaled(imgLabels_[0][1]->size(), Qt::KeepAspectRatio));
	imgLabels_[1][1]->setPixmap(QPixmap::fromImage(trajectory_->getDirection()).scaled(imgLabels_[1][1]->size(), Qt::KeepAspectRatio));
}

void MainWindow::editTracks() {
	TracksEditor *editor = new TracksEditor(&trajectories_, this);
	editor->setModal(true);
	editor->show();
}

void MainWindow::open() {	
	fileSave_ = QFileDialog::getOpenFileName(this, tr("Otwórz plik trajektorii"),
										  QDir::currentPath(),
										  tr("XML Files (*.xml)"));
	if (fileSave_.isEmpty())
		return;

	QFile file(fileSave_);
	if (!file.open(QFile::ReadOnly | QFile::Text)) {
		QMessageBox::warning(this, tr("XML Trajektorie"),
								tr("Nie można odczytać %1:\n%2.")
								.arg(fileSave_)
								.arg(file.errorString()));
		return;
	}

	vector<Trajectory> tracks_tmp(trajectories_);
	trajectories_.clear();
	XmlTrajectoryReader reader(&trajectories_);
	if (reader.read(&file)) {
		statusBar()->showMessage(tr("Plik odczytany"), 2000);
	}
	else {
		QMessageBox::critical(this, tr("Błąd odczytu"),
						  tr("Plik %1 ma niewłaściwą strukturę.").arg(fileSave_));
		trajectories_ = tracks_tmp;
	}
}

void MainWindow::save() {
	if (fileSave_.isEmpty()) {
		saveAs();
	}
	else {
		QFile file(fileSave_);
		if (!file.open(QFile::WriteOnly | QFile::Text)) {
			QMessageBox::warning(this, tr("XML Trajektorie"),
								 tr("Nie można otworzyć pliku %1:\n%2.")
								 .arg(fileSave_)
								 .arg(file.errorString()));
			return;
		}
		XmlTrajectoryWriter writer(&trajectories_);
		writer.write(&file);
		saved_ = true;
		statusBar()->showMessage("Zapisano plik", 2000);
	}
}

void MainWindow::saveAs() {
	fileSave_ = QFileDialog::getSaveFileName(this, tr("Zapisz plik"),
											QDir::currentPath(),
											tr("XML Files (*.xml)"));
	if (fileSave_.isEmpty())
		return;

	save();
}

void MainWindow::newFile() {
	if (!trajectories_.empty()) {
		if (QMessageBox::question(this, tr("Niezapisany plik"),
							 tr("Czy chcesz zapisać zmiany?"),
							 QMessageBox::Save | QMessageBox::Cancel) == QMessageBox::Save) {
			save();
		}
		trajectories_.clear();
	}
}

void MainWindow::showStatics() {
	if (!staticWindow_) {
		staticWindow_ = new StaticGestureDialog(this);
		connect(staticWindow_, SIGNAL(finished(int)), this, SLOT(closeStatics()));
		connect(staticWindow_, SIGNAL(capture()), this, SLOT(captureStatic()));
		captureAct_->setEnabled(true);
	}
	disconnect(staticAct_, SIGNAL(triggered()), this, SLOT(showStatics()));
	staticAct_->setText(tr("Wyłącz statyczne gesty"));
	connect(staticAct_, SIGNAL(triggered()), this, SLOT(closeStatics()));
	staticWindow_->show();
}

void MainWindow::closeStatics() {
	if (!staticWindow_)
		return;
	disconnect(staticAct_, SIGNAL(triggered()), this, SLOT(closeStatics()));
	staticAct_->setText(tr("Rejestruj statyczne gesty"));
	connect(staticAct_, SIGNAL(triggered()), this, SLOT(showStatics()));
	staticWindow_->close();
}

void MainWindow::captureStatic() {
	QString path, gesture_title;
	if (!staticWindow_) {
		QMessageBox::warning(this, tr("Brak ustawień przechwytywania"),
							 tr("Ustaw parametry przechwytywnia"));
		showStatics();
		return;
	}
	if (!trajectory_->getTrackingState()) {
		QMessageBox::warning(this, tr("Błąd przechwytywania"),
							 tr("Śledzenie jest nieaktywne. Aktywuj śledzenie trajektorii by przechwycić gest."));
		return;
	}
	path = staticWindow_->getPath().trimmed();
	gesture_title = staticWindow_->getGesture().trimmed();
	if (path.isEmpty()) {
		QMessageBox::warning(this, tr("Nie ustawiono parametrów"),
							 tr("Ustaw ścieżkę zapisu danych"));
		showStatics();
		return;
	}
	if (gesture_title.isEmpty()) {
		QMessageBox::warning(this, tr("Nie ustawiono parametrów"),
							 tr("Ustaw edykietę gestu"));
		showStatics();
		return;
	}
	int id = 0;
	QString p;
	while (1) {
		p = path + QDir::separator() + gesture_title + tr("_") + QString::number(id) + tr(".bmp");
		if (!QFile::exists(p))
			break;
		id++;
	}
	trajectory_->saveShape(p);
}

void MainWindow::hmm() {
	QString hmm_file = QFileDialog::getOpenFileName(this, tr("Otwórz plik trajektorii dla HMM"),
										  QDir::currentPath(),
										  tr("XML Files (*.xml)"));
	if (hmm_file.isEmpty())
		return;

	QFile file(hmm_file);
	if (!file.open(QFile::ReadOnly | QFile::Text)) {
		QMessageBox::warning(this, tr("XML Trajektorie"),
								tr("Nie można odczytać %1:\n%2.")
								.arg(hmm_file)
								.arg(file.errorString()));
		return;
	}

	HmmStatesDialog *states_dialog = new HmmStatesDialog(this);
	if (states_dialog->exec() != QDialog::Accepted) {
		return;
	}
	int states_count = states_dialog->getStates();

	vector<Trajectory> hmm_tracks;
	XmlTrajectoryReader reader(&hmm_tracks);
	if (reader.read(&file)) {
		statusBar()->showMessage(tr("Plik trajektorii dla HMM odczytany"), 2000);
	}
	else {
		QMessageBox::critical(this, tr("Błąd odczytu"),
						  tr("Plik %1 ma niewłaściwą strukturę.").arg(hmm_file));
		return;
	}

	vector<vector<int> > data_train, garbage_train;
	vector<string> types;
	Trajectory::getTrajectoryTypes(hmm_tracks, types);
	vector<Hmm*> models;
	HMMProgressDialog *dialog = new HMMProgressDialog(this);
	dialog->setProgress(0);
	dialog->show();
	for (vector<string>::iterator it = types.begin(); it<types.end();) {
		if (*it == "garbage" || *it == "undefined") {
			it = types.erase(it);
		}
		else {
			++it;
		}
	}
	dialog->setProgress(1);

	for (size_t k=0; k<types.size(); k++) {
		data_train.clear();
		if (types[k] == "garbage" || types[k] == "undefined")
			continue;
		int tracks_count = 0;
		for (size_t i=0; i<hmm_tracks.size(); i++) {
			if (QString::fromStdString(hmm_tracks[i].type_).toLower() == QString::fromStdString(types[k]).toLower()) {
				tracks_count++;
			}
		}
		int count = 0;
		for (size_t i=0; i<hmm_tracks.size(); i++) {
			if (QString::fromStdString(hmm_tracks[i].type_).toLower() == QString::fromStdString(types[k]).toLower()) {
				count++;
				garbage_train.push_back(hmm_tracks[i].data_);
				data_train.push_back(hmm_tracks[i].data_);
			}
		}
		models.push_back(new Hmm(states_count, 16));
		models[k]->baumWelsh(data_train);
		dialog->setProgress(100*(k+1)/(types.size()+1));
	}
	Hmm* garbage = new Hmm(states_count,16);
	garbage->baumWelsh(garbage_train);
	dialog->setProgress(100);
	dialog->close();
	delete dialog;

	QString folder = QFileDialog::getExistingDirectory(this, "Wybierz folder do zapisu modeli", QDir::currentPath());
	if (!folder.isEmpty()) {
		for (size_t i=0; i<types.size(); i++) {
			QString path = folder + QDir::separator() + QString::fromStdString(types[i]) + "_model.xml";
			models[i]->write(path);
		}
		QString path = folder + QDir::separator() + "garbage_model.xml";
		garbage->write(path);
	}

	delete garbage;
	for (size_t i=0; i<models.size(); i++) {
		delete models[i];
	}
}

void MainWindow::trainStatic() {
	MainStaticGestureDialog *dialog = new MainStaticGestureDialog(this);
	dialog->show();
}
